#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>



namespace ns_blockqueue
{
    const int default_cap = 5;
    template<class T>
    class blockqueue
    {
    private:
        std::queue<T> _bq; //阻塞队列
        int _cap;  //队列容量
        pthread_mutex_t mymutex; //队列的锁
        //条件变量
        pthread_cond_t _is_full; //生产者判断队列是否满  
        pthread_cond_t _is_empty; //消费者判断队列是否空  
    

    public:
        blockqueue(const int cap = default_cap):_cap(cap)
        {
            //初始化锁和条件变量
            pthread_mutex_init(&mymutex,nullptr);
            pthread_cond_init(&_is_full,nullptr);
            pthread_cond_init(&_is_empty,nullptr);
        }
        ~blockqueue()
        {
            //销毁锁和条件变量
            pthread_mutex_destroy(&mymutex);
            pthread_cond_destroy(&_is_full);
            pthread_cond_destroy(&_is_empty);
        }

    private:
        void LockQueue()
        {
            pthread_mutex_lock(&mymutex);
        }
        void UnLockQueue()
        {
            pthread_mutex_unlock(&mymutex);
        }

        bool Is_full()
        {
            return _bq.size() == _cap;
        }
        
        bool Is_empyu()
        {
            return _bq.size() == 0;
        }

        void ProducterWait()//生产者等待
        {
            pthread_cond_wait(&_is_empty,&mymutex); //让生产者等待
        }
        void ConsumerWait() //消费者等待
        {
            pthread_cond_wait(&_is_full,&mymutex); //让消费者等待
        }

        //唤醒生产者，由消费者调用，因为只有消费者知道产品空了
        void WakeupProducter()
        {
            pthread_cond_signal(&_is_empty); //唤醒消费者
        }

        //唤醒消费者，由生产者调用，因为只有生产者知道自己上架了产品
        void WakeupConsumer()
        {
            pthread_cond_signal(&_is_full); //唤醒生产者
        }

    public:

        void push(const T& in)
        {
            //生产者
            //锁住队列
            LockQueue();

            //我们的线程是有锁的，所以需要等待条件变量
            while(Is_full()) //队列满了则停止生产
            {
                ProducterWait(); //满了，等待消费者Pop
            }
            //生产数据
                _bq.push(in);

            //解锁
            UnLockQueue();
            //唤醒消费者
            WakeupConsumer();

        }

        void pop(T* out)
        {
            //消费者

            //上锁
            LockQueue(); 

            //如果队列为空，则没有数据可以拿
            while(Is_empyu())
            {
                //等待生产者放数据
                ConsumerWait(); 
            }
            //出数据
            *out = _bq.front();
            _bq.pop(); 
            //解锁
            UnLockQueue();
            
            //唤醒生产者
            WakeupProducter();

        }
    };


}
